#include "OsgQuickWindow.h"
#include "OsgQuickView.h"

namespace OsgQtQuick
{

    int mouseButton(QMouseEvent* event);
    int swapKey(QKeyEvent* event);

    OsgQuickView::OsgQuickView(QQuickItem* parent)
    {
        setFlag(ItemHasContents, true);
        setAcceptHoverEvents(true);
        setAcceptedMouseButtons(Qt::AllButtons);

        m_ptrContext = new osgViewer::GraphicsWindowEmbedded(0, 0, 1, 1);

        connect(this, &QQuickItem::windowChanged, this, &OsgQuickView::handleWindowChanged);
    }

    void OsgQuickView::acceptWindow(OsgQuickWindow* window)
    {
        m_pWindow = window;

        if(!m_ptrView) m_ptrView = new osgViewer::View;

        m_ptrView->getCamera()->setGraphicsContext(m_ptrContext);// m_pWindow->getGraphicsContext());
        m_ptrView->getEventQueue()->setGraphicsContext(m_ptrContext);// m_pWindow->getGraphicsContext());

        updateViewport();

        m_pWindow->addView(this);
    }

    osgViewer::View* OsgQuickView::getView()
    {
        return m_ptrView.get();
    }

    void OsgQuickView::updateViewport()
    {
        if (!m_ptrView || !window()) return;

        QSize windowSize = window()->size() * window()->devicePixelRatio();
        QSize size = QSize(this->width(), this->height()) * window()->devicePixelRatio();

        double x = this->x() * window()->devicePixelRatio();
        double y = this->y() * window()->devicePixelRatio();

        //m_ptrView->getCamera()->getGraphicsContext()->resizedImplementation(0, 0, windowSize.width(), windowSize.height());
        m_ptrContext->resizedImplementation(0, 0, windowSize.width(), windowSize.height());

        m_ptrView->getEventQueue()->windowResize(x, y, size.width(), size.height());
        m_ptrView->getCamera()->setViewport(x, y, size.width(), size.height());
        m_ptrView->getCamera()->setProjectionMatrixAsPerspective(30.0f, 
            static_cast<double>(size.width()) / static_cast<double>(size.height()), 1.0f, 10000.0f);

    }

    void OsgQuickView::handleWindowChanged(QQuickWindow* win)
    {
        if (win) {
            if (m_pWindow = OsgQtQuick::OsgQuickWindow::fromWindow(win))
            {
                acceptWindow(m_pWindow);
            }
        }
    }

    void OsgQuickView::setKeyboardModifiers(QInputEvent* event)
    {
        if (!m_ptrView) return;

        int modkey = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier);
        unsigned int mask = 0;
        if (modkey & Qt::ShiftModifier) mask |= osgGA::GUIEventAdapter::MODKEY_SHIFT;
        if (modkey & Qt::ControlModifier) mask |= osgGA::GUIEventAdapter::MODKEY_CTRL;
        if (modkey & Qt::AltModifier) mask |= osgGA::GUIEventAdapter::MODKEY_ALT;
        m_ptrView->getEventQueue()->getCurrentEventState()->setModKeyMask(mask);
    }

    QPointF OsgQuickView::mousePoint(QMouseEvent* event)
    {
        qreal x = event->x() * window()->devicePixelRatio();
        qreal y = event->y() * window()->devicePixelRatio();

        return QPointF(x, y);
    }

	void OsgQuickView::mousePressEvent(QMouseEvent* event)
	{
        if (!m_ptrView) return;
        int button = mouseButton(event);
        setKeyboardModifiers(event);
        QPointF pos = mousePoint(event);
        m_ptrView->getEventQueue()->mouseButtonPress(pos.x(), pos.y(), button);
	}

    void OsgQuickView::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry)
    {
        updateViewport();
        BaseClass::geometryChange(newGeometry, oldGeometry);
    }

	void OsgQuickView::mouseMoveEvent(QMouseEvent* event)
	{
        if (!m_ptrView) return;
        setKeyboardModifiers(event);
        QPointF pos = mousePoint(event);
        m_ptrView->getEventQueue()->mouseMotion(pos.x(), pos.y());
	}

	void OsgQuickView::mouseReleaseEvent(QMouseEvent* event)
	{
        if (!m_ptrView) return;
        int button = mouseButton(event);
        setKeyboardModifiers(event);
        QPointF pos = mousePoint(event);
        m_ptrView->getEventQueue()->mouseButtonRelease(pos.x(), pos.y(), button);
	}

	void OsgQuickView::mouseDoubleClickEvent(QMouseEvent* event)
	{
        if (!m_ptrView) return;
        int button = mouseButton(event);
        setKeyboardModifiers(event);
        QPointF pos(event->x(), height() - event->y());
        m_ptrView->getEventQueue()->mouseDoubleButtonPress(pos.x(), pos.y(), button);
	}

	void OsgQuickView::hoverMoveEvent(QHoverEvent* event)
	{
        if (!m_ptrView) return;
        setKeyboardModifiers(event);
        QPointF pos(event->posF().x(), height() - event->posF().y());
        m_ptrView->getEventQueue()->mouseMotion(pos.x(), pos.y());
	}

	void OsgQuickView::hoverEnterEvent(QHoverEvent* event)
	{
        if (!m_ptrView) return;
        this->setFocus(true);
	}

	void OsgQuickView::wheelEvent(QWheelEvent* event)
	{
        if (!m_ptrView) return;
        if (event->angleDelta().y() > 0)
        {
            m_ptrView->getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_UP);
        }
        else if (event->angleDelta().y() < 0)
        {
            m_ptrView->getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_DOWN);
        }

        if (event->angleDelta().x() > 0)
        {
            m_ptrView->getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_LEFT);
        }
        else if (event->angleDelta().x() < 0)
        {
            m_ptrView->getEventQueue()->mouseScroll(osgGA::GUIEventAdapter::SCROLL_RIGHT);
        }
	}

	void OsgQuickView::keyPressEvent(QKeyEvent* event)
	{
        if (!m_ptrView) return;
       setKeyboardModifiers(event);
       m_ptrView->getEventQueue()->keyPress(swapKey(event));
	}

	void OsgQuickView::keyReleaseEvent(QKeyEvent* event)
	{
        if (!m_ptrView) return;
        if (event->isAutoRepeat())
        {
            event->ignore();
        }
        else
        {
            setKeyboardModifiers(event);
            m_ptrView->getEventQueue()->keyRelease(swapKey(event));
        }
	}

    int mouseButton(QMouseEvent* event)
    {
        int button = 0;
        switch (event->button())
        {
        case Qt::LeftButton: button = 1; break;
        case Qt::MiddleButton: button = 2; break;
        case Qt::RightButton: button = 3; break;
        case Qt::NoButton: button = 0; break;
        default: button = 0; break;
        }

        return button;
    }

    std::map<unsigned int, int> makeKeyMap()
    {
        std::map<unsigned int, int> map;

        map[Qt::Key_Escape] = osgGA::GUIEventAdapter::KEY_Escape;
        map[Qt::Key_Delete] = osgGA::GUIEventAdapter::KEY_Delete;
        map[Qt::Key_Home] = osgGA::GUIEventAdapter::KEY_Home;
        map[Qt::Key_Enter] = osgGA::GUIEventAdapter::KEY_KP_Enter;
        map[Qt::Key_End] = osgGA::GUIEventAdapter::KEY_End;
        map[Qt::Key_Return] = osgGA::GUIEventAdapter::KEY_Return;
        map[Qt::Key_PageUp] = osgGA::GUIEventAdapter::KEY_Page_Up;
        map[Qt::Key_PageDown] = osgGA::GUIEventAdapter::KEY_Page_Down;
        map[Qt::Key_Left] = osgGA::GUIEventAdapter::KEY_Left;
        map[Qt::Key_Right] = osgGA::GUIEventAdapter::KEY_Right;
        map[Qt::Key_Up] = osgGA::GUIEventAdapter::KEY_Up;
        map[Qt::Key_Down] = osgGA::GUIEventAdapter::KEY_Down;
        map[Qt::Key_Backspace] = osgGA::GUIEventAdapter::KEY_BackSpace;
        map[Qt::Key_Tab] = osgGA::GUIEventAdapter::KEY_Tab;
        map[Qt::Key_Space] = osgGA::GUIEventAdapter::KEY_Space;
        map[Qt::Key_Delete] = osgGA::GUIEventAdapter::KEY_Delete;
        map[Qt::Key_Alt] = osgGA::GUIEventAdapter::KEY_Alt_L;
        map[Qt::Key_Shift] = osgGA::GUIEventAdapter::KEY_Shift_L;
        map[Qt::Key_Control] = osgGA::GUIEventAdapter::KEY_Control_L;
        map[Qt::Key_Meta] = osgGA::GUIEventAdapter::KEY_Meta_L;

        map[Qt::Key_F1] = osgGA::GUIEventAdapter::KEY_F1;
        map[Qt::Key_F2] = osgGA::GUIEventAdapter::KEY_F2;
        map[Qt::Key_F3] = osgGA::GUIEventAdapter::KEY_F3;
        map[Qt::Key_F4] = osgGA::GUIEventAdapter::KEY_F4;
        map[Qt::Key_F5] = osgGA::GUIEventAdapter::KEY_F5;
        map[Qt::Key_F6] = osgGA::GUIEventAdapter::KEY_F6;
        map[Qt::Key_F7] = osgGA::GUIEventAdapter::KEY_F7;
        map[Qt::Key_F8] = osgGA::GUIEventAdapter::KEY_F8;
        map[Qt::Key_F9] = osgGA::GUIEventAdapter::KEY_F9;
        map[Qt::Key_F10] = osgGA::GUIEventAdapter::KEY_F10;
        map[Qt::Key_F11] = osgGA::GUIEventAdapter::KEY_F11;
        map[Qt::Key_F12] = osgGA::GUIEventAdapter::KEY_F12;
        map[Qt::Key_F13] = osgGA::GUIEventAdapter::KEY_F13;
        map[Qt::Key_F14] = osgGA::GUIEventAdapter::KEY_F14;
        map[Qt::Key_F15] = osgGA::GUIEventAdapter::KEY_F15;
        map[Qt::Key_F16] = osgGA::GUIEventAdapter::KEY_F16;
        map[Qt::Key_F17] = osgGA::GUIEventAdapter::KEY_F17;
        map[Qt::Key_F18] = osgGA::GUIEventAdapter::KEY_F18;
        map[Qt::Key_F19] = osgGA::GUIEventAdapter::KEY_F19;
        map[Qt::Key_F20] = osgGA::GUIEventAdapter::KEY_F20;

        map[Qt::Key_hyphen] = '-';
        map[Qt::Key_Equal] = '=';

        map[Qt::Key_division] = osgGA::GUIEventAdapter::KEY_KP_Divide;
        map[Qt::Key_multiply] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
        map[Qt::Key_Minus] = '-';
        map[Qt::Key_Plus] = '+';
        //map[Qt::Key_H             ] = osgGA::GUIEventAdapter::KEY_KP_Home     ;
        //map[Qt::Key_              ] = osgGA::GUIEventAdapter::KEY_KP_Up       ;
        //map[92                    ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up  ;
        //map[86                    ] = osgGA::GUIEventAdapter::KEY_KP_Left     ;
        //map[87                    ] = osgGA::GUIEventAdapter::KEY_KP_Begin    ;
        //map[88                    ] = osgGA::GUIEventAdapter::KEY_KP_Right    ;
        //map[83                    ] = osgGA::GUIEventAdapter::KEY_KP_End      ;
        //map[84                    ] = osgGA::GUIEventAdapter::KEY_KP_Down     ;
        //map[85                    ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down;
        map[Qt::Key_Insert] = osgGA::GUIEventAdapter::KEY_KP_Insert;
        //map[Qt::Key_Delete        ] = osgGA::GUIEventAdapter::KEY_KP_Delete   ;

        return map;
    }

    std::map<unsigned int, int> keyMap(makeKeyMap());

    int swapKey(QKeyEvent* event)
    {
        std::map<unsigned int, int>::iterator it =
            keyMap.find(event->key());
        return it == keyMap.end() ?
            static_cast<int>(*(event->text().toLatin1().data())) :
            it->second;
    }
}
